namespace Yaaf.WP7.NativeAccess.IO.Internals.AllDevices
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Linq;

    using Yaaf.WP7.NativeAccess.Helper;

    public class LimitedFileSystemAccess : IFileSystemAccess
    {
        private IsolatedStorageFile storage;

        public LimitedFileSystemAccess()
        {
            storage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
        }

        public virtual Stream OpenFile(string path, FileMode mode, FileAccess access)
        {
            var isolatedPath = GetIsolated(path);
            return storage.OpenFile(isolatedPath, mode, access);
        }

        public virtual bool ExistsFile(string path)
        {
            var isolatedPath = GetIsolated(path);
            return storage.FileExists(isolatedPath);
        }

        private string GetIsolated(string path)
        {
            if (!PathInfo.IsInIsolatedStorage(path))
            {
                throw new UnauthorizedAccessException("this path (" + path + ") is not accessible");
            }

            return PathInfo.ToIsolatedStoragePath(path);
        }

        public virtual void MoveFile(string path, string dest, bool overwrite)
        {
            var isolatedSource = GetIsolated(path);
            var isolatedDest = GetIsolated(dest);

            storage.MoveFile(isolatedSource, isolatedDest);
        }

        public void DeleteFile(string path)
        {
            var isolatedPath = GetIsolated(path);
            storage.DeleteFile(isolatedPath);
        }

        public virtual void CopyFile(string source, string dest, bool overwrite)
        {
            var isolatedSource = GetIsolated(source);
            var isolatedDest = GetIsolated(dest);

            storage.CopyFile(isolatedSource, isolatedDest);
        }

        public virtual long GetFileSize(string fullName)
        {
            var isolatedPath = GetIsolated(fullName);
            try
            {
                using (var file = storage.OpenFile(isolatedPath, FileMode.Open, FileAccess.Read))
                {
                    return file.Length;
                }
            }
            catch (IsolatedStorageException)
            {
                return 0;
            }
        }

        public virtual bool ExistsDirectory(string fullName)
        {
            if (PathInfo.RootIsolatedStorage.StartsWith(fullName))
            {
                return true;
            }

            var isolatedPath = GetIsolated(fullName);
            return storage.DirectoryExists(isolatedPath);
        }

        public virtual PathInformation GetPathInformation(string path)
        {
            if (PathInfo.RootIsolatedStorage.StartsWith(path))
            {
                return PathInformation.Directory;
            }

            var isolated = GetIsolated(path);
            if (storage.FileExists(isolated))
            {
                return PathInformation.File;
            }

            if (storage.DirectoryExists(isolated))
            {
                return PathInformation.Directory;
            }

            return PathInformation.NotExistent;
        }

        public virtual IEnumerable<FileSystemEntryStruct> EnumerateFileSystemEntries(string path, bool includeFiles, bool includeDirectories, string search, bool fullPath)
        {
            if (string.IsNullOrEmpty(path))
            {
                path = "\\";
            }
            if (PathInfo.RootIsolatedStorage.StartsWith(path) && PathInfo.RootIsolatedStorage.Length > path.Length)
            {
                if (includeDirectories)
                {
                    var lastPart = PathInfo.RootIsolatedStorage.Substring(path.Length);
                    var splitts = lastPart.Trim('\\').Split('\\');
                    var folderName = splitts[0];

                    if (string.IsNullOrEmpty(search) || search == "*")
                    {
                        if (fullPath)
                        {
                            var full = Path.Combine(path, folderName);
                            //if (!full.StartsWith("\\"))
                            //{
                            //    full = "\\" + full;
                            //}
                            return new FileSystemEntryStruct[] { new FileSystemEntryStruct(full, false) };
                        }
                        else
                        {
                            return new FileSystemEntryStruct[] { new FileSystemEntryStruct(folderName, false) };
                        }
                    }
                    else
                    {
                        throw new NotImplementedException("search is not supported jet!");
                    }
                }
                else
                {
                    return new FileSystemEntryStruct[0];
                }
            }

            var isolated = GetIsolated(path);
            if (!string.IsNullOrEmpty(isolated) && !isolated.EndsWith("\\"))
            {
                isolated += "\\";
            }

            if (string.IsNullOrEmpty(search))
            {
                isolated += "*";
            }
            else
            {
                isolated += search;
            }

            List<FileSystemEntryStruct> elements = new List<FileSystemEntryStruct>();
            Func<string, bool, FileSystemEntryStruct> selector;
            if (fullPath)
            {
                selector = (s, isFile) => new FileSystemEntryStruct(Path.Combine(path, s), isFile);
            }
            else
            {
                selector = (s, isFile) => new FileSystemEntryStruct(s, isFile);
            }

            if (includeDirectories)
            {
                elements.AddRange(storage.GetDirectoryNames(isolated).Select(s => selector(s, false)));
            }

            if (includeFiles)
            {
                elements.AddRange(storage.GetFileNames(isolated).Select(s => selector(s, true)));
            }

            return elements;
        }

        public virtual void CreateDirectory(string fullName)
        {
            var isolated = GetIsolated(fullName);
            storage.CreateDirectory(isolated);
        }

        public virtual  void DeleteDirectory(string fullName, bool recursive)
        {
            var isolated = GetIsolated(fullName);
            if (!recursive)
            {
                storage.DeleteDirectory(isolated);
            }
            else
            {
                DeleteDirectoryRecursive(isolated);
            }
        }

        private void DeleteDirectoryRecursive(string isolated)
        {
            if (isolated == null)
            {
                isolated = "";
            }
            var dirPath = isolated;
            if (!isolated.EndsWith("\\"))
            {
                if (!string.IsNullOrEmpty(isolated))
                {
                    isolated += "\\";
                }
            }
            else
            {
                dirPath = isolated.Substring(0, isolated.Length - 1);
            }

            var search = isolated + "*";
            foreach (var fileName in storage.GetFileNames(search))
            {
                storage.DeleteFile(isolated + fileName);
            }

            foreach (var directoryName in storage.GetDirectoryNames(search))
            {
                DeleteDirectoryRecursive(isolated + directoryName);
            }

            storage.DeleteDirectory(dirPath);
        }

        public virtual void ExecuteFile(string fullName)
        {
            throw new UnauthorizedAccessException("execution of files is not allowed!");
        }
    }
}